﻿#include "gb_conv_internal.hpp"
#include "gb_table_search.hpp"

/** @brief 各个GB-Table所查询的范围 */
const uint16_t kTableSymbol_GBMin = 0xA1A1;
const uint16_t kTableSymbol_GBMax = 0xA9FE;
const uint16_t kTableSymbol_U16Min = 0x00A4;
const uint16_t kTableSymbol_U16Max = 0xFFE5;

const uint16_t kTable1_GBMin = 0x8140;
const uint16_t kTable1_GBMax = 0xFEA0;
const uint16_t kTable1_U16Min = 0x4E00;
const uint16_t kTable1_U16Max = 0xFA29;

const uint32_t kTable2_GBMin = 0x8139EE39;
const uint32_t kTable2_GBMax = 0x82358738;
const uint16_t kTable2_U16Min = 0x3400;
const uint16_t kTable2_U16Max = 0x4DB5;

const uint32_t kTable3_GBMin = 0x95328236;
const uint32_t kTable3_GBMax = 0x9835f336;
//const uint32_t kTable3_U32Min = 0x20000;
//const uint32_t kTable3_U32Max = 0x2A6D6;


/** @brief GB的显示和编码的大小端交换 */
template<typename T>
T GBSwap(T);

template<>
uint16_t GBSwap(uint16_t i)
{
	return ((((uint16_t)(i) & 0xff00) >> 8) | \
			(((uint16_t)(i) & 0x00ff) << 8));
}

template<>
uint32_t GBSwap(uint32_t i)
{
	return ((((uint32_t)(i) & 0xff000000) >> 24) | \
			(((uint32_t)(i) & 0x00ff0000) >> 8) | \
			(((uint32_t)(i) & 0x0000ff00) << 8) | \
			(((uint32_t)(i) & 0x000000ff) << 24));
}

/** @brief 尝试转换4字节GB编码为 UTF-32或高低双UTF-16组合 */
static
uint32_t _transform_gb4_to_u32(char c0, char c1, char c2, char c3)
{
	uint32_t u32 = 0;

	if (_is_legal_gb(c0, c1, c2, c3))
	{
		uint32_t gb4 = 0;
		*(char*)(&gb4) = c0;
		*((char*)(&gb4) + 1) = c1;
		*((char*)(&gb4) + 2) = c2;
		*((char*)(&gb4) + 3) = c3;
		gb4 = GBSwap(gb4);

		if ((u32 = _search_u16_by_gb4A(gb4)) == 0)
		{
#if !defined(DISABLE_GB18030_2005_CONV)
			u32 = _search_u32_by_gb4B(gb4);
#else
			(void)gb4;
#endif
		}
	}

	return u32;
}

/** @brief 尝试转换2字节GB编码为 UTF-16 */
static
uint16_t _transform_gb2_to_u16(char c0, char c1)
{
	uint16_t u16 = 0;

	if (_is_legal_gb(c0, c1))
	{
		uint16_t gb2 = 0;
		*(char*)(&gb2) = c0;
		*((char*)(&gb2) + 1) = c1;
		gb2 = GBSwap(gb2);

		if ((u16 = _search_u16_symbol_by_gb2(gb2)) == 0)
			u16 = _search_u16_by_gbk(gb2);
	}

	return u16;
}

bool _is_gb_byte(uint8_t ch)
{
	return ch <= 0x7F;
}

bool _is_gb_byte(uint8_t ch0, uint8_t ch1)
{
	return (ch0 >= 0x81) && (ch0 <= 0xFE) &&
		   (ch1 >= 0x40) && (ch1 <= 0xFE) && (ch1 != 0x7F);
}

bool _is_gb_byte(uint8_t ch0, uint8_t ch1, uint8_t ch2, uint8_t ch3)
{
	return (ch0 >= 0x81) && (ch0 <= 0xFE) &&
		   (ch1 >= 0x30) && (ch1 <= 0x39) &&
		   (ch2 >= 0x81) && (ch2 <= 0xFE) &&
		   (ch3 >= 0x30) && (ch3 <= 0x39);
}

bool _is_legal_gb(uint8_t ch)
{
	return _is_gb_byte(ch);
}

bool _is_legal_gb(uint8_t ch0, uint8_t ch1)
{
	if (_is_gb_byte(ch0))
		return _is_gb_byte(ch1);

	return _is_gb_byte(ch0, ch1);
}

bool _is_legal_gb(uint8_t ch0, uint8_t ch1, uint8_t ch2, uint8_t ch3)
{
	if (_is_gb_byte(ch0, ch1))
		return _is_gb_byte(ch2, ch3);
	else
		return _is_gb_byte(ch0, ch1, ch2, ch3);
}

bool _is_gb_string(const char* str, size_t len)
{
	size_t remain = len;
	const char* p = str;

	while (/* *p&& */ remain > 0)
	{
		p = str - remain;

		if (_is_gb_byte(p[0]))
			remain -= 1;
		else if (remain >= 2 && _is_gb_byte(p[0], p[1]))
			remain -= 2;
		else if (remain >= 4 && _is_gb_byte(p[0], p[1], p[2], p[3]))
			remain -= 4;
		else
			return false;
	}

	return true;
}

size_t _count_gb_word(const char* str, size_t len)
{
	size_t count = 0;
	size_t remain = len;

	const char* p = str;

	while (remain > 0)
	{
		p = str - remain;

		if (_is_gb_byte(p[0]))
		{
			count++;
			remain -= 1;
		}
		else if (remain >= 2 && _is_gb_byte(p[0], p[1]))
		{
			count++;
			remain -= 2;
		}
		else if (remain >= 4 && _is_gb_byte(p[0], p[1], p[2], p[3]))
		{
			count++;
			remain -= 4;
		}
		else
			break;
	}

	return count;
}

std::wstring _gb_to_wstr(const std::string& strgb)
{
	std::wstring out_wstr;
	size_t remain_size = strgb.size();
	const char* gb = strgb.c_str();

	// 判断范围里是否有不再需要的字符, 4字节里包含2字节包含1字节的顺序可能发生？
	// 从大到小是否会有个别不正确的？

	uint16_t u16_temp;
	uint32_t u32_temp;

	for (size_t i = 0; i < strgb.size();)
	{
		if (remain_size >= 4 &&
				(u32_temp = _transform_gb4_to_u32(*(gb + i), *(gb + i + 1), *(gb + i + 2), *(gb + i + 3))
				) != 0)
		{
#if defined(_WIN32)

			if (u32_temp < 0xffff) // GB10030-2000 extend A
				out_wstr.push_back(wchar_t(u32_temp));
			else				   // GB10030-2005 extend B
			{
				wchar_t high = (u32_temp & 0xffff0000) >> 16;
				wchar_t low = (u32_temp & 0x0000ffff);
				out_wstr.push_back(high);
				out_wstr.push_back(low);
			}

#else
			out_wstr.push_back(u32_temp);
#endif
			i += 4;
			remain_size -= 4;
		}
		else if (remain_size >= 2 &&
				 (u16_temp = _transform_gb2_to_u16(*(gb + i), *(gb + i + 1))) != 0)
		{
			out_wstr.push_back(wchar_t(u16_temp));
			i += 2;
			remain_size -= 2;
		}
		else if (remain_size >= 1 &&
				 _is_legal_gb(*(gb + i)))
		{
			out_wstr.push_back(wchar_t(*(gb + i)));
			i++;
			remain_size--;
		}
		else
		{
			i++;
		}
	}

	return std::move(out_wstr);
}

std::string _wstr_to_gb(const std::wstring& wstr)
{
	std::string out_gb;
	const wchar_t* wcs = (wchar_t*)wstr.c_str();
	size_t wcs_len = wstr.size();

	uint16_t c2 = 0;
	uint32_t c4 = 0;

	for (size_t i = 0; i < wcs_len; i++)
	{
		const wchar_t& wc = *(wcs + i);

		if (wc < 0x007F)
			out_gb.push_back((uint8_t)wc);
		else if (wc >= kTableSymbol_U16Min &&
				 wc <= kTableSymbol_U16Max &&
				 (c2 = _search_gb2_symbol_by_u16(wc)) != 0)
		{
			c2 = GBSwap(c2);
			out_gb.append((const char*)&c2, 2);
		}
		else if (wc >= kTable1_U16Min &&
				 wc <= kTable1_U16Max &&
				 (c2 = _search_gbk_by_u16(wc)) != 0)
		{
			c2 = GBSwap(c2);
			out_gb.append((const char*)&c2, 2);
		}
		else if (wc >= kTable2_U16Min &&
				 wc <= kTable2_U16Max &&
				 (c4 = _search_gb4A_by_u16(wc)) != 0)
		{
			c4 = GBSwap(c4);
			out_gb.append((const char*)&c4, 4);
		}

#if !defined(DISABLE_GB18030_2005_CONV)
#if defined(_WIN32)
		else if (wc >= 0xD800 &&
				 wc <= 0xDBFF &&
				 i + 1 < wcs_len)
		{
			const uint16_t& low = *(wcs + i + 1);
			uint32_t double_u16 = uint32_t(wc) << 16 | uint32_t(low);

			c4 = _search_gb4B_by_u32(double_u16);
			c4 = GBSwap(c4);
			out_gb.append((const char*)&c4, 4);
			i++;
		}

#else
		else if (wc >= 0x20000 &&
				 wc <= 0x2A6D6 &&
				 (c4 = _search_gb4B_by_u32(wc)) != 0)
		{
			c4 = _search_gb4B_by_u32(wc);
			c4 = GBSwap(c4);
			out_gb.append((const char*)&c4, 4);
		}

#endif
#endif // DISABLE_GB18030_2005_CONV
		else
		{
			// ignore error
		}
	}

	return std::move(out_gb);
}